package Utils.word;

import Utils.data.StringUtils;
import Utils.io.IOUtils;
import Utils.word.dataTypes.InsertType;
import Utils.word.dataTypes.ProcessResult;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;

/**
 * Word Poi Utils
 *
 * @author d11yu
 */
public final class PoiUtils {
    private PoiUtils() {
    }

    public static final String PATH_NUMBERING = "word/numbering.xml";
    public static final String PATH_STYLES = "word/styles.xml";

    /**
     * 插入word文档
     *
     * @param paraTgt                        目标段落
     * @param pathSrc                        待插入文件路径
     * @param type                           插入方式
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static Boolean insertFile(XWPFParagraph paraTgt, String pathSrc, InsertType type, Boolean removeLastBlankParaBehindTable) throws IOException {
        if (pathSrc == null) {
            MarkerCommandParser.process(paraTgt);
            return false;
        }
        XWPFDocument doc = null;
        File fileSrc = new File(pathSrc);
        if (fileSrc.exists()) {
            try (FileInputStream is = new FileInputStream(fileSrc)) {
                doc = new XWPFDocument(is);
//                copyNumbering(paraTgt.getDocument(), fileSrc);
                return insertFile(paraTgt, doc, type, removeLastBlankParaBehindTable);
            } catch (IOException | XmlException e) {
                e.printStackTrace();
                return false;
            } /*finally {
                if (doc != null) {
                    doc.close();
                }
            }*/
        }
        return false;
    }

    /**
     * 插入word文档
     *
     * @param paraTgt                        目标段落
     * @param fileSrc                        待插入文件
     * @param type                           插入方式
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static Boolean insertFile(XWPFParagraph paraTgt, File fileSrc, InsertType type, Boolean removeLastBlankParaBehindTable) throws IOException {
        if (fileSrc == null) {
            MarkerCommandParser.process(paraTgt);
            return false;
        }
        XWPFDocument docSrc = null;
        try (FileInputStream is = new FileInputStream(fileSrc)) {
            docSrc = new XWPFDocument(is);
            insertFile(paraTgt, docSrc, type, removeLastBlankParaBehindTable);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 插入word文档
     *
     * @param paraTgt                        目标段落
     * @param docSrc                         待插入文件
     * @param type                           插入方式
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static Boolean insertFile(XWPFParagraph paraTgt, XWPFDocument docSrc, InsertType type, Boolean removeLastBlankParaBehindTable) throws XmlException, IOException {
        if (docSrc == null) {
            MarkerCommandParser.process(paraTgt);
            return false;
        }

        // 处理命令
        ProcessResult result = MarkerCommandParser.process(paraTgt);
        if (result == ProcessResult.DELETED_CONTENT || result == ProcessResult.DELETED_MARKERS) {
            return false;
        }

        XWPFDocument docTgt = paraTgt.getDocument();
        XmlCursor[] cursor = new XmlCursor[]{paraTgt.getCTP().newCursor()};
        int[] pos = new int[]{docTgt.getPosOfParagraph(paraTgt)};
        List<IBodyElement> es = docSrc.getBodyElements();

        // 添加style
//        docTgt.createStyles().setStyles(docSrc.getStyle()); // 不好用，避免使用

        switch (type) {
            case BEFORE:
                for (int i = 0; i < es.size() - 1; i++) {
                    method1(es.get(i), docTgt, cursor, pos);
                }

                if (removeLastBlankParaBehindTable && es.size() >= 2 && es.get(es.size() - 2) instanceof XWPFTable) {
                    if (es.get(es.size() - 1) instanceof XWPFParagraph) {
                        XWPFParagraph lastPara = (XWPFParagraph) es.get(es.size() - 1);
                        if ("".equals(lastPara.getText().trim())) {
                            break;
                        }
                    }
                }
                method1(es.get(es.size() - 1), docTgt, cursor, pos);
                break;
            case REPLACE:
                for (int i = 0; i < es.size() - 1; i++) {
                    method1(es.get(i), docTgt, cursor, pos);
                }

                if (removeLastBlankParaBehindTable && es.size() >= 2 && es.get(es.size() - 2) instanceof XWPFTable) {
                    if (es.get(es.size() - 1) instanceof XWPFParagraph) {
                        XWPFParagraph lastPara = (XWPFParagraph) es.get(es.size() - 1);
                        if ("".equals(lastPara.getText().trim())) {
                            deleteBodyElement(paraTgt);
                            break;
                        }
                    }
                }
                method1(es.get(es.size() - 1), docTgt, cursor, pos);
                deleteBodyElement(paraTgt);
                break;
            case AFTER:
                for (int i = 0; i < es.size(); i++) {
                    cursor[0].toNextSibling();
                    method1(es.get(i), docTgt, cursor, pos);
                }

                if (removeLastBlankParaBehindTable && es.size() >= 2 && es.get(es.size() - 2) instanceof XWPFTable) {
                    if (es.get(es.size() - 1) instanceof XWPFParagraph) {
                        XWPFParagraph lastPara = (XWPFParagraph) es.get(es.size() - 1);
                        if ("".equals(lastPara.getText().trim())) {
                            break;
                        }
                    }
                }
                method1(es.get(es.size() - 1), docTgt, cursor, pos);
                break;
            default:
                break;
        }
        cursor[0].dispose();
        return true;
    }

    /**
     * 复制numbering到目标文件，不好用，避免使用。
     *
     * @param docTgt
     * @param pathSrc
     * @throws IOException
     * @throws XmlException
     */
    @Deprecated
    public static void copyNumbering(XWPFDocument docTgt, String pathSrc) {
        try (InputStream is = IOUtils.getInputStreamFromZipFile(pathSrc, PoiUtils.PATH_NUMBERING)) {
            CTNumbering ctNumbering = CTNumbering.Factory.parse(is);
            docTgt.createNumbering().setNumbering(ctNumbering);
        } catch (XmlException | IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 复制numbering到目标文件，不好用，避免使用。
     *
     * @param docTgt
     * @param fileSrc
     * @throws IOException
     * @throws XmlException
     */
    @Deprecated
    public static void copyNumbering(XWPFDocument docTgt, File fileSrc) {
        try (InputStream is = IOUtils.getInputStreamFromZipFile(new ZipFile(fileSrc), PoiUtils.PATH_NUMBERING)) {
            CTNumbering ctNumbering = CTNumbering.Factory.parse(is);
            docTgt.createNumbering().setNumbering(ctNumbering);
        } catch (XmlException | IOException e) {
            e.printStackTrace();
        }
    }

    private static void method1(IBodyElement e, XWPFDocument docTgt, XmlCursor[] cursor, int[] pos) {
        if (e instanceof XWPFParagraph) {
            insertParagraphBeforeCursor(docTgt, cursor[0], (XWPFParagraph) e);
        } else if (e instanceof XWPFTable) {
            insertTableBeforeCursor(docTgt, cursor[0], (XWPFTable) e);
        }
        IBodyElement ele = docTgt.getBodyElements().get(++pos[0]);
        if (ele instanceof XWPFParagraph) {
            cursor[0] = ((XWPFParagraph) ele).getCTP().newCursor();
        } else if (ele instanceof XWPFTable) {
            cursor[0] = ((XWPFTable) ele).getCTTbl().newCursor();
        }
    }

    /**
     * 根据1个标记在word文档中定位其所在位置，并返回Paragraph，只返回第一个匹配项。
     *
     * @param doc
     * @param marker
     * @return 如果未匹配到，则返回null。
     */
    public static XWPFParagraph locateParagraphByOneMarker(XWPFDocument doc, String marker) {
        return locateParagraphByOneMarker(doc, marker, -1, false);
    }

    /**
     * 根据1个标记在word文档中定位其所在位置，并返回Paragraph，只返回第一个匹配项。
     *
     * @param doc
     * @param marker
     * @param start  查询起始段落序号，默认为-1，如果顺序查询，则会转化为0；如果倒查，则会转化为size-1。
     * @return 如果未匹配到，则返回null。
     */
    public static XWPFParagraph locateParagraphByOneMarker(XWPFDocument doc, String marker, Integer start) {
        return locateParagraphByOneMarker(doc, marker, start, false);
    }

    /**
     * 根据1个标记在word文档中定位其所在位置，并返回Paragraph，只返回第一个匹配项。
     *
     * @param doc
     * @param markerRegx
     * @param start      查询起始段落序号，默认为-1，如果顺序查询，则会转化为0；如果倒查，则会转化为size-1。
     * @param reverse    是否倒查，默认为false。
     * @return 如果未匹配到，则返回null。
     */
    public static XWPFParagraph locateParagraphByOneMarker(XWPFDocument doc, String markerRegx, Integer start, Boolean reverse) {
        List<IBodyElement> elements = doc.getBodyElements();
        if (!reverse) {
            if (start == -1) {
                start = 0;
            }
            for (int i = start; i < elements.size(); i++) {
                if (elements.get(i) instanceof XWPFTable) {
                    continue;
                }
                XWPFParagraph p = (XWPFParagraph) elements.get(i);
//                if (elements.get(i).getText().contains(markerRegx)) {
                if (StringUtils.isMatch(p.getText(), markerRegx)) {
                    return p;
                }
            }
        } else {
            if (start == -1) {
                start = elements.size() - 1;
            }
            for (int i = start; i >= 0; i--) {
                if (elements.get(i) instanceof XWPFTable) {
                    continue;
                }
                XWPFParagraph p = (XWPFParagraph) elements.get(i);
//                if (elements.get(i).getText().contains(markerRegx)) {
                if (StringUtils.isMatch(p.getText(), markerRegx)) {
                    return p;
                }
            }
        }

        return null;
    }

    /**
     * 根据2个标记在word文档中定位其所在位置，并返回IBodyElement列表，只返回第一个匹配项。
     *
     * @param doc
     * @param marker1Regex 头标签匹配规则，或头标签
     * @param marker2Regex 尾标签匹配规则，或尾标签
     * @param start        起始查询位置
     * @param reverse      是否倒查
     * @return 返回列表，如果未查询到，则返回null。
     */
    public static List<IBodyElement> locateBodyElementsByTwoMarkers(XWPFDocument doc, String marker1Regex, String marker2Regex, Integer start, Boolean reverse) {
        XWPFParagraph paraStart = locateParagraphByOneMarker(doc, marker1Regex, start, reverse);
        if (paraStart == null) {
            return null;
        }

        int start1 = doc.getPosOfParagraph(paraStart) + 1;
        XWPFParagraph paraEnd = locateParagraphByOneMarker(doc, marker2Regex, start1);
        if (paraEnd == null) {
            return null;
        }

        List<IBodyElement> eles = new ArrayList<>();
        for (int i = doc.getPosOfParagraph(paraStart); i <= doc.getPosOfParagraph(paraEnd); i++) {
            eles.add(doc.getBodyElements().get(i));
        }

        if (eles.size() != 0) {
            return eles;
        } else {
            return null;
        }
    }

    /**
     * 批量插入Word文档，按单标签。
     *
     * @param doc
     * @param paths                          待插入的word文档路径map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static void insertFilesByOneMarker_Path(XWPFDocument doc, Map<String, String> paths, Boolean removeLastBlankParaBehindTable) {
        List<XWPFParagraph> paras = doc.getParagraphs();
        for (int i = paras.size() - 1; i >= 0; i--) {
            String header = StringUtils.getFirstMatch(paras.get(i).getText(), MarkerCommandParser.MARKER1_CONTENT_REGEX);
            if (header == null) {
                continue;
            }
            try {
                insertFile(paras.get(i), paths.get(header), InsertType.REPLACE, removeLastBlankParaBehindTable);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量插入Word文档，按单标签。
     *
     * @param doc
     * @param files                          待插入的word文档map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static void insertFilesByOneMarker(XWPFDocument doc, Map<String, File> files, Boolean removeLastBlankParaBehindTable) {
        List<XWPFParagraph> paras = doc.getParagraphs();
        for (int i = paras.size() - 1; i >= 0; i--) {
            String header = StringUtils.getFirstMatch(paras.get(i).getText(), MarkerCommandParser.MARKER1_CONTENT_REGEX);
            if (header == null) {
                continue;
            }
            try {
                insertFile(paras.get(i), files.get(header), InsertType.REPLACE, removeLastBlankParaBehindTable);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量插入Word文档，按双标签。
     *
     * @param doc
     * @param paths                          待插入的word文档路径map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    @Deprecated
    public static void insertFilesByTwoMarkers_Path(XWPFDocument doc, Map<String, String> paths, Boolean removeLastBlankParaBehindTable) {
        List<XWPFParagraph> paras = doc.getParagraphs();
        for (int i = paras.size() - 1; i >= 0; i--) {
            String header = StringUtils.getFirstMatch(paras.get(i).getText(), MarkerCommandParser.MARKER2_HEAD_HEADER_REGEX);
            if (header == null) {
                continue;
            }
            String marker1 = MarkerCommandParser.getMarker2HeadRegexWithHeader(header);
            String marker2 = MarkerCommandParser.getMarker2TailRegexWithHeader(header);
            List<IBodyElement> eleMarker = locateBodyElementsByTwoMarkers(doc, marker1, marker2, i, false);
            try {
                if (insertFile(paras.get(i), paths.get(header), InsertType.BEFORE, removeLastBlankParaBehindTable)) {
                    deleteBodyElements(eleMarker);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量插入Word文档，按双标签。
     *
     * @param doc
     * @param files                          待插入的word文档map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static void insertFilesByTwoMarkers(XWPFDocument doc, Map<String, File> files, Boolean removeLastBlankParaBehindTable) {
        List<XWPFParagraph> paras = doc.getParagraphs();
        for (int i = paras.size() - 1; i >= 0; i--) {
            String header = StringUtils.getFirstMatch(paras.get(i).getText(), MarkerCommandParser.MARKER2_HEAD_HEADER_REGEX);
            if (header == null) {
                continue;
            }
            String marker1 = MarkerCommandParser.getMarker2HeadRegexWithHeader(header);
            String marker2 = MarkerCommandParser.getMarker2TailRegexWithHeader(header);
            List<IBodyElement> eleMarker = locateBodyElementsByTwoMarkers(doc, marker1, marker2, i, false);
            try {
                if (insertFile(paras.get(i), files.get(header), InsertType.BEFORE, removeLastBlankParaBehindTable)) {
                    deleteBodyElements(eleMarker);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量插入Word文档，单、双标签都考虑。
     *
     * @param doc
     * @param files                          待插入的word文档map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static void insertFiles(XWPFDocument doc, Map<String, File> files, Boolean removeLastBlankParaBehindTable) {
        // 先处理单标签
        insertFilesByOneMarker(doc, files, removeLastBlankParaBehindTable);
        // 再处理双标签
        insertFilesByTwoMarkers(doc, files, removeLastBlankParaBehindTable);
    }

    /**
     * 批量插入Word文档，单、双标签都考虑。
     *
     * @param fileTgt                        目标文件
     * @param files                          待插入的word文档map
     * @param removeLastBlankParaBehindTable 是否删除最后一个表格下的空段落
     */
    public static void insertFiles(File fileTgt, Map<String, File> files, Boolean removeLastBlankParaBehindTable) throws IOException {
        XWPFDocument doc = null;
        try (FileInputStream is = new FileInputStream(fileTgt)) {
            doc = new XWPFDocument(is);
            insertFiles(doc, files, removeLastBlankParaBehindTable);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (doc != null) {
                doc.close();
            }
        }
    }

    /**
     * 替换段落
     *
     * @param paraTgt 目标段落
     * @param paraSrc 待插入段落
     */
    public static void replaceParagraph(XWPFParagraph paraTgt, XWPFParagraph paraSrc) {
        XWPFDocument doc = paraTgt.getDocument();
        XmlCursor cursor0 = paraTgt.getCTP().newCursor();
        insertParagraphBeforeCursor(doc, cursor0, paraSrc);
        deleteBodyElement(paraTgt);
        cursor0.dispose();
    }

    /**
     * 在段落前方插入段落
     *
     * @param paraTgt 目标段落
     * @param paraSrc 待插入段落
     */
    public static void insertBeforeParagraph(XWPFParagraph paraTgt, XWPFParagraph paraSrc) {
        XWPFDocument doc = paraTgt.getDocument();
        XmlCursor cursor0 = paraTgt.getCTP().newCursor();
        insertParagraphBeforeCursor(doc, cursor0, paraSrc);
        cursor0.dispose();
    }

    /**
     * 在cursor前插入段落
     *
     * @param doc
     * @param cursor
     * @param paraSrc
     */
    private static void insertParagraphBeforeCursor(XWPFDocument doc, XmlCursor cursor, XWPFParagraph paraSrc) {
        XWPFParagraph paraNew = doc.insertNewParagraph(cursor);
        paraNew.getCTP().setPPr(paraSrc.getCTP().getPPr());

        for (XWPFRun run : paraSrc.getRuns()) {
            XWPFRun runNew = paraNew.createRun();
            CTR ctr = run.getCTR();
            runNew.getCTR().setRPr(ctr.getRPr());
            runNew.setText(run.text());

            // 处理break
            for (CTBr ctBr : ctr.getBrList()) {
                String s = ctBr.getType().toString();
                if ("page".equals(s)) {
                    runNew.addBreak(BreakType.PAGE);
                } else if ("column".equals(s)) {
                    runNew.addBreak(BreakType.COLUMN);
                } else if ("textWrapping".equals(s)) {
                    runNew.addBreak(BreakType.TEXT_WRAPPING);
                }
            }

            // 处理tab
            for (CTEmpty ctEmpty : ctr.getTabList()) {
                runNew.addTab();
            }
        }
    }

    /**
     * 在cursor处插入table
     *
     * @param doc
     * @param cursor
     * @param t0
     */
    private static void insertTableBeforeCursor(XWPFDocument doc, XmlCursor cursor, XWPFTable t0) {
        XWPFTable t1 = doc.insertNewTbl(cursor);
        copyTable(t0, t1);
    }

    public static void copyTable(XWPFTable t0, XWPFTable t1) {
        t1.getCTTbl().setTblPr(t0.getCTTbl().getTblPr());
        t1.getCTTbl().setTblGrid(t0.getCTTbl().getTblGrid());

        //region 处理边框
        CTTblBorders borders0 = t0.getCTTbl().getTblPr().getTblBorders();
        if (borders0 == null) {
            // TODO: 2022-10-25 尝试注释掉
            /*CTTblBorders borders1 = t1.getCTTbl().getTblPr().addNewTblBorders();
            borders1.addNewBottom().setVal(STBorder.SINGLE);
            borders1.addNewLeft().setVal(STBorder.SINGLE);
            borders1.addNewRight().setVal(STBorder.SINGLE);
            borders1.addNewTop().setVal(STBorder.SINGLE);
            borders1.addNewInsideH().setVal(STBorder.SINGLE);
            borders1.addNewInsideV().setVal(STBorder.SINGLE);*/
        } else {
            t1.setBottomBorder(t0.getBottomBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getBottomBorderType(), t0.getBottomBorderSize() == -1 ? 1 : t0.getBottomBorderSize(), t0.getBottomBorderSpace() == -1 ? 1 : t0.getBottomBorderSpace(), t0.getBottomBorderColor() == null ? "auto" : t0.getBottomBorderColor());
            t1.setLeftBorder(t0.getLeftBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getLeftBorderType(), t0.getLeftBorderSize() == -1 ? 1 : t0.getLeftBorderSize(), t0.getLeftBorderSpace() == -1 ? 1 : t0.getLeftBorderSpace(), t0.getLeftBorderColor() == null ? "auto" : t0.getLeftBorderColor());
            t1.setRightBorder(t0.getRightBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getRightBorderType(), t0.getRightBorderSize() == -1 ? 1 : t0.getRightBorderSize(), t0.getRightBorderSpace() == -1 ? 1 : t0.getRightBorderSpace(), t0.getRightBorderColor() == null ? "auto" : t0.getRightBorderColor());
            t1.setTopBorder(t0.getTopBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getTopBorderType(), t0.getTopBorderSize() == -1 ? 1 : t0.getTopBorderSize(), t0.getTopBorderSpace() == -1 ? 1 : t0.getTopBorderSpace(), t0.getTopBorderColor() == null ? "auto" : t0.getTopBorderColor());
            t1.setInsideHBorder(t0.getInsideHBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getInsideHBorderType(), t0.getInsideHBorderSize() == -1 ? 1 : t0.getInsideHBorderSize(), t0.getInsideHBorderSpace() == -1 ? 1 : t0.getInsideHBorderSpace(), t0.getInsideHBorderColor() == null ? "auto" : t0.getInsideHBorderColor());
            t1.setInsideVBorder(t0.getInsideVBorderType() == null ? XWPFTable.XWPFBorderType.SINGLE : t0.getInsideVBorderType(), t0.getInsideVBorderSize() == -1 ? 1 : t0.getInsideVBorderSize(), t0.getInsideVBorderSpace() == -1 ? 1 : t0.getInsideVBorderSpace(), t0.getInsideVBorderColor() == null ? "auto" : t0.getInsideVBorderColor());
        }
        //endregion

        List<XWPFTableRow> rows0 = t0.getRows();
        for (int i = 0; i < rows0.size(); i++) {
            XWPFTableRow r0 = rows0.get(i);
            XWPFTableRow r1 = t1.createRow();
            r1.getCtRow().setTrPr(r0.getCtRow().getTrPr());
            CTTblPrEx tblPrEx0 = r0.getCtRow().getTblPrEx();
            if (tblPrEx0 != null) {
                r1.getCtRow().setTblPrEx(tblPrEx0);
            }
            for (int j = 0; j < r0.getTableCells().size(); j++) {
                XWPFTableCell c1 = j == 0 ? r1.getTableCells().get(0) : r1.createCell();
                XWPFTableCell c0 = r0.getTableCells().get(j);
                c1.getCTTc().setTcPr(c0.getCTTc().getTcPr());
                XmlCursor cur = c1.getParagraphArray(0).getCTP().newCursor();
                for (int k = 0; k < c0.getBodyElements().size(); k++) {
                    IBodyElement e0 = c0.getBodyElements().get(k);
                    if (e0 instanceof XWPFParagraph) {
                        XWPFParagraph p1 = c1.insertNewParagraph(cur);
                        cur.toNextToken();
                        XWPFParagraph p0 = (XWPFParagraph) e0;
                        copyParagraph(p0, p1);
                    } else if (e0 instanceof XWPFTable) {
                        XWPFTable tab1 = c1.insertNewTbl(cur);
                        XWPFTable tab0 = (XWPFTable) e0;
                        copyTable(tab0, tab1);
                    }
                }
                // 移除默认段
                c1.removeParagraph(c1.getParagraphs().size() - 1);
            }
        }
        // 移除默认行
        t1.removeRow(0);
    }

    /**
     * 将段落复制到目标段落
     *
     * @param source
     * @param target
     */
    public static void copyParagraph(XWPFParagraph source, XWPFParagraph target) {
        target.getCTP().setPPr(source.getCTP().getPPr());
        for (int i = 0; i < source.getRuns().size(); i++) {
            XWPFRun run = source.getRuns().get(i);
            XWPFRun targetRun = target.createRun();
            targetRun.getCTR().setRPr(run.getCTR().getRPr());
            targetRun.setText(run.text());
        }
    }

    /**
     * 在段落后方插入段落
     *
     * @param paraTgt 目标段落
     * @param paraSrc 待插入段落
     */
    public static void insertAfterParagraph(XWPFParagraph paraTgt, XWPFParagraph paraSrc) {
        XWPFDocument doc = paraTgt.getDocument();
        XmlCursor cursor0 = paraTgt.getCTP().newCursor();
        cursor0.toNextSibling();
        insertParagraphBeforeCursor(doc, cursor0, paraSrc);
        cursor0.dispose();
    }

    /**
     * 删除paragraph
     *
     * @param ele
     * @return
     */
    public static Boolean deleteBodyElement(IBodyElement ele) {
        XWPFDocument doc = ele.getBody().getXWPFDocument();
        if (ele instanceof XWPFParagraph) {
            return doc.removeBodyElement(doc.getPosOfParagraph((XWPFParagraph) ele));
        } else if (ele instanceof XWPFTable) {
            return doc.removeBodyElement(doc.getPosOfTable((XWPFTable) ele));
        }
        return false;
    }

    public static List<Boolean> deleteBodyElements(List<? extends IBodyElement> eles) {
        List<Boolean> res = new ArrayList<>();
        for (IBodyElement ele : eles) {
            res.add(deleteBodyElement(ele));
        }
        return res;
    }

    /**
     * 删除run
     *
     * @param run
     * @return
     */
    public static boolean deleteRun(XWPFRun run) {
        XWPFParagraph paragraph = (XWPFParagraph) run.getParent();
        for (int i = 0; i < paragraph.getRuns().size(); i++) {
            if (run == paragraph.getRuns().get(i)) {
                return paragraph.removeRun(i);
            }
        }
        return false;
    }

    /**
     * 返回IBodyElement的文字。
     *
     * @param ele
     * @return 如果既不是XWPFParagraph也不是XWPFTable，则返回null。
     */
    public static String getBodyElementText(IBodyElement ele) {
        if (ele == null) {
            return null;
        }
        if (ele instanceof XWPFParagraph) {
            return ((XWPFParagraph) ele).getText();
        } else if (ele instanceof XWPFTable) {
            return ((XWPFTable) ele).getText();
        }
        return null;
    }
}
